package com.gitee.l0km.aocache.plugin;

import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.LocalVariablesSorter;

import com.gitee.l0km.aocache.AspecjrtDecorator;
import com.gitee.l0km.aocache.aop.AocacheAnnotatedAspect;

/**
 * <h3>JoinPoint包装注入器 - AOP字节码增强核心模块</h3>
 * 
 * <p>本模块通过ASM字节码操作技术，在编译期对切面方法进行增强，实现JoinPoint参数的自动包装和智能处理。</p>
 *
 * <h3>核心功能</h3>
 * <ul>
 *   <li>🔧 <b>动态参数包装</b>：自动识别并包装方法参数中的JoinPoint/ProceedingJoinPoint实例</li>
 *   <li>🔄 <b>上下文保持</b>：确保参数位置、类型、顺序与原始方法完全一致</li>
 *   <li>⚡ <b>性能优化</b>：采用最小变量分配策略，避免栈帧膨胀导致的性能损耗</li>
 *   <li>🛡️ <b>类型安全</b>：精确识别JoinPoint类型体系，避免错误包装非目标参数</li>
 * </ul>
 *
 * <h3>技术特性</h3>
 * <ul>
 *   <li>基于ASM 9.x字节码操作框架实现</li>
 *   <li>支持Spring AOP/AspectJ等多种切面编程模型</li>
 *   <li>采用逆向参数扫描算法提升处理效率</li>
 *   <li>内置线程安全的类型描述符重写机制</li>
 * </ul>
 *
 * <h3>工作原理</h3>
 * <p>通过方法访问器模式({@link MethodVisitor})对目标类进行以下增强：</p>
 * <ol>
 *   <li>扫描方法参数的JoinPoint类型</li>
 *   <li>动态分配局部变量暂存关联参数</li>
 *   <li>注入包装器实例和方法调用指令</li>
 *   <li>重构方法参数栈保持执行环境一致性</li>
 * </ol>
 *
 * <h3>使用场景</h3>
 * <ul>
 *   <li>需要增强JoinPoint的切面方法</li>
 *   <li>缓存注解的元数据扩展场景</li>
 *   <li>分布式跟踪的上下文传递需求</li>
 *   <li>需要方法拦截元数据扩展的AOP场景</li>
 * </ul>
 *
 * <h3>注意事项</h3>
 * <ul>
 *   <li>需配合{@link LocalVariablesSorter}使用以保证变量分配安全</li>
 *   <li>目标类需包含包装器类的静态引用</li>
 *   <li>建议在类加载期进行字节码增强</li>
 * </ul>
 *
 * @see org.objectweb.asm.ClassVisitor
 * @see org.objectweb.asm.commons.LocalVariablesSorter
 */
public class JoinPointWrapInjecter extends ClassVisitor {
	/**
	 * 表示 JoinPoint 类的类型常量。
	 * 该常量用于在字节码操作中标识 JoinPoint 类型。
	 */
	private static final Type JOINPOINT_TYPE = Type.getType(JoinPoint.class);
	/**
	 * 表示 ProceedingJoinPoint 类型的常量，用于在切面编程中处理切入点(JoinPoint)。
	 * 该类型用于获取方法执行的上下文信息。
	 */
	private static final Type PROCEEDJOINPOINT_TYPE = Type.getType(ProceedingJoinPoint.class);
	/**
	 * 表示目标切面类的内部类名常量，使用 AocacheAnnotatedAspect 类的类型信息获取。
	 * 该常量用于在切面注入过程中标识目标切面。
	 */
	private static final String TARGET_ASPECT = Type.getType(AocacheAnnotatedAspect.class).getInternalName();
	/**
	 * {@link  AspecjrtDecorator} 的内部类名常量
	 */
	private static final String DECORATOR_CLASS = Type.getType(AspecjrtDecorator.class).getInternalName();
	/**
	 * 表示用于包装方法（{@link  AspecjrtDecorator#wrap(Object)}）的名称。
	 */
	private static final String WRAP_METHOD = "wrap";
	/**
	 * 表示包装方法（{@link  AspecjrtDecorator#wrap(Object)}）的参数描述的常量字符串。
	 */
	private static final String WRAP_DESC;
	static {
		try {
			Method method = AspecjrtDecorator.class.getMethod(WRAP_METHOD, Object.class);
			WRAP_DESC = Type.getMethodDescriptor(method);
		} catch (NoSuchMethodException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 表示原始包名的字符串。
	 */
	private final String originalPackage;
	/**
	 * 存储重定位包的名称。
	 */
	private final String relocatedPackage;
	private boolean ignored = true;

	/**
	 * 构造方法。
	 *
	 * @param cv               ClassVisitor 实例，用于访问类的结构。
	 * @param originalPackage  原始包名，用于标识类的原始位置。
	 * @param relocatedPackage 重定位包名，用于指定类的新位置。
	 */
	public JoinPointWrapInjecter(ClassVisitor cv, String originalPackage, String relocatedPackage) {
		super(Opcodes.ASM9, cv);
		this.originalPackage = originalPackage;
		this.relocatedPackage = relocatedPackage;
	}
	private String relocatedOf(String classname) {
		if (classname.startsWith(originalPackage)) {
			return relocatedPackage + classname.substring(originalPackage.length());
		}
		return classname;
	}
	/** 统一包名替换逻辑 */
	private String relocate(String input) {
		if (input != null) {
			return input.replace(originalPackage, relocatedPackage);
		}
		return input;
	}
	boolean isIgnored() {
		return ignored;
	}

	@Override
	public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
		MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
		// 构建访问器链
		LocalVariablesSorter lvs = new LocalVariablesSorter(access, desc, mv);
		return new AspectMethodVisitor(Opcodes.ASM9, lvs); 
	}
	/**
	 * AspectMethodVisitor 是用于实现 AOP 切面方法参数动态包装的 ASM 字节码访问器。
	 * 核心功能是在检测到切面方法调用时，对 JoinPoint 类型参数进行智能包装处理，实现透明的参数增强。
	 *
	 * <h3>实现原理</h3>
	 * 1. <b>参数定位机制</b>：<br>
	 *    - 通过方法描述符解析参数类型数组（Type[] paramTypes）<br>
	 *    - 使用 {@link #findLastJoinPointParam} 逆向扫描定位最后一个 JoinPoint 类型参数<br>
	 *
	 * 2. <b>参数暂存系统</b>：<br>
	 *    - 对目标参数右侧的非 JoinPoint 参数进行暂存（{@link #storePrams(Type[], int)}）<br>
	 *    - 使用 {@link LocalVariablesSorter} 分配新的局部变量槽位<br>
	 *    - 生成 ISTORE/ASTORE 等字节码指令保存参数值<br>
	 *
	 * 3. <b>动态包装处理</b>：<br>
	 *    - 调用 {@link #wrapJoinPointArgument(Type)} 生成包装逻辑：<br>
	 *      • 插入 GETSTATIC 获取包装器实例<br>
	 *      • 生成方法调用指令调用包装方法<br>
	 *    - 使用 ASM 的 visitMethodInsn 插入包装方法调用<br>
	 *
	 * 4. <b>参数恢复机制</b>：
	 *    - 通过 {@link #reloadParams(List)} 按 FILO 顺序恢复暂存参数<br>
	 *    - 生成 ILOAD/ALOAD 等字节码指令加载参数值<br>
	 *    - 保持原始参数顺序和类型一致性<br>
	 *
	 * <h3>核心功能</h3>
	 * ✅ JoinPoint 参数自动包装：将原始 JoinPoint 实例替换为增强后的包装实例<br>
	 * ✅ 参数位置无关处理：支持任意位置的 JoinPoint 参数（包括多参数场景）<br>
	 * ✅ 类型安全处理：精确识别 JoinPoint/ProceedingJoinPoint 及其子类<br>
	 * ✅ 字节码级优化：最小化局部变量使用，避免栈帧大小异常<br>
	 *
	 * <h3>典型处理流程（以方法参数列表 [A, B, JoinPoint, C] 为例）</h3>
	 * <ol>
	 *   <li>定位最后一个JoinPoint参数（索引=2）</li>
	 *   <li>暂存右侧参数C到局部变量</li>
	 *   <li>对索引2的参数执行包装：wrap(JoinPoint) → WrappedJoinPoint</li>
	 *   <li>重新加载暂存的参数C</li>
	 *   <li>生成新参数列表 [A, B, WrappedJoinPoint, C]</li>
	 * </ol>
	 *
	 * <h3>关键技术点</h3>
	 * <ul>
	 *   <li>逆向参数扫描策略：提高多JoinPoint参数场景的处理效率</li>
	 *   <li>局部变量动态分配：通过{@link LocalVariablesSorter}避免变量冲突</li>
	 *   <li>类型描述符重写：配合{@link JoinPointWrapInjecter#relocate}实现包路径适配</li>
	 *   <li>异常安全处理：确保字节码栈平衡不受包装逻辑影响</li>
	 * </ul>
	 *
	 * @see org.objectweb.asm.commons.LocalVariablesSorter
	 * @see com.gitee.l0km.aocache.plugin.JoinPointWrapInjecter
	 */
	private class AspectMethodVisitor extends MethodVisitor {
		private final LocalVariablesSorter localVariablesSorter;

		public AspectMethodVisitor(int api, MethodVisitor mv) {
			super(api, mv);
			this.localVariablesSorter = (LocalVariablesSorter) mv;
		}
		/**
		 * 处理方法调用指令，实现JoinPoint参数包装逻辑<br>
		 * 
		 * <p><b>处理逻辑：</b></p>
		 * <ul>
		 *   <li>识别目标切面方法调用（{@link #TARGET_ASPECT}）</li>
		 *   <li>执行JoinPoint参数包装预处理（{@link #processJoinPointWrap}）</li>
		 *   <li>重写方法描述符适配包路径（{@link #relocate}）</li>
		 * </ul>
		 *
		 * @param opcode 方法调用操作码（INVOKESTATIC/INVOKEVIRTUAL等）
		 * @param owner  目标类内部名称（ASM格式，如"com/example/ClassName"）
		 * @param name   调用方法名称
		 * @param desc   方法描述符（包含参数和返回类型）
		 * @param itf    指示方法是否属于接口
		 */
		@Override
		public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
			// 检测AocacheAnnotatedAspect方法调用
			if (owner.equals(TARGET_ASPECT) && processJoinPointWrap(desc)) {
				ignored = false;
				// 调用原始方法
				super.visitMethodInsn(opcode, owner, name, relocate(desc), itf);
			}else {
				super.visitMethodInsn(opcode, owner, name, desc, itf);
			}
		}

		/**
		 * 处理切面方法中JoinPoint参数的包装逻辑。<br>
		 *
		 * 实现步骤：
		 * 1. 从参数列表中从右向左查找最后一个JoinPoint/ProceedingJoinPoint类型参数的位置
		 * 2. 将该参数右侧的所有非JoinPoint参数暂存到局部变量中
		 * 3. 对找到的JoinPoint参数执行包装逻辑（调用AspecjrtDecorator.wrap方法）
		 * 4. 将暂存的参数按原始顺序重新加载到栈中
		 * @param desc   方法描述符，格式为"(参数类型)返回类型"，例如："(Ljava/lang/String;I)V"
		 * 
		 * @return 当方法参数中包含JoinPoint/ProceedingJoinPoint类型参数时返回true，否则返回false
		 * 
		 * @see #findLastJoinPointParam(Type[])  用于定位JoinPoint参数的辅助方法
		 * @see #storePrams(Type[], int)        用于参数暂存的辅助方法
		 * @see #wrapJoinPointArgument(Type)    执行实际包装操作的辅助方法
		 * @see #reloadParams(List)             用于参数恢复的辅助方法
		 */
		private boolean processJoinPointWrap(String desc) {
			Type[] paramTypes = Type.getArgumentTypes(desc);
			int joinPointIndex = findLastJoinPointParam(paramTypes);
			if (joinPointIndex == -1) {
				return false;
			}

			List<LocalVar> savedParams = storePrams(paramTypes, joinPointIndex);

			wrapJoinPointArgument(paramTypes[joinPointIndex]);

			reloadParams(savedParams);

			return true;
		}

		/**
		 * 插入包装JoinPoint参数的wrap方法调用指令，并将调用返回对象转换为重定位对象
		 * 
		 * @param argType 需要包装的JoinPoint参数类型，通过该参数确定目标装饰类型
		 */
		private void wrapJoinPointArgument(Type argType) {
			/*
			 * 插入INVOKESTATIC指令，调用包装方法完成参数包装
			 */
			visitMethodInsn(Opcodes.INVOKESTATIC,
					DECORATOR_CLASS,
					WRAP_METHOD,
					WRAP_DESC,
					false);

			/*
			 * 对包装后的对象进行类型转换，转换到重定义后的内部类型
			 */
			visitTypeInsn(Opcodes.CHECKCAST, relocatedOf(argType.getInternalName()));
		}
		private int findLastJoinPointParam(Type[] paramTypes) {
			for (int i = paramTypes.length - 1; i >= 0; i--) {
				if (paramTypes[i].equals(JOINPOINT_TYPE) || paramTypes[i].equals(PROCEEDJOINPOINT_TYPE)) {
					return i;
				}
			}
			return -1;
		}

		/**
		 * 从右到左出栈，存储右侧参数<br>
		 * 将出栈的参数存储到给定参数类型的局部变量，并将其插入到列表的头部。
		 *
		 * @param paramTypes  参数类型数组
		 * @param beforeIndex 指定的索引，表示在此索引之前的参数将不被存储
		 * @return 存储的局部变量列表
		 */
		private List<LocalVar> storePrams(Type[] paramTypes, int beforeIndex) {
			LinkedList<LocalVar> params = new LinkedList<>();
			for (int i = paramTypes.length - 1; i > beforeIndex; i--) {
				Type type = paramTypes[i];
				/** 创建新的局部变量 */
				int varIndex = localVariablesSorter.newLocal(type);
				super.visitVarInsn(type.getOpcode(Opcodes.ISTORE), varIndex);
				/** 存储参数，插入到列表头部 */
				params.add(0, new LocalVar(type, varIndex));
			}
			return params;
		}

		/**
		 * 从左到右入栈，恢复右侧参数<br>
		 * 
		 * @param params 包含要重新加载的局部变量的列表
		 */
		private void reloadParams(List<LocalVar> params) {
			for (LocalVar var : params) {
				super.visitVarInsn(var.type.getOpcode(Opcodes.ILOAD), var.index);
			}
		}
		private class LocalVar {
			final Type type;
			final int index;
			LocalVar(Type type, int index) {
				this.type = type;
				this.index = index;
			}
		}
	}
}
